package com.atguigu.gulimall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gulimall.search.config.ElasticsearchConfig;
import com.atguigu.gulimall.search.constant.EsConstant;
import com.atguigu.gulimall.search.dto.SearchDto;
import com.atguigu.gulimall.search.service.SearchService;
import com.atguigu.gulimall.search.vo.SearchResultVo;
import com.guigui.common.dto.elastic.SkusElasticModel;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.*;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author xin
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient client;

    @Override
    public SearchResultVo search(SearchDto searchDto) {

        SearchResultVo result = null;

        SearchRequest searchRequest = buildSearchRequest(searchDto);

        System.out.println("DSL 语句\t" + searchRequest.source());
        try {
            SearchResponse search = client.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);
            result = resolverResponse(search, searchDto);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 构建查询请求
     * @param searchDto
     * @return
     */
    private SearchRequest buildSearchRequest(SearchDto searchDto) {
        /**
         *  查询：
         */
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        if (searchDto.getKeyword() != null) {
            queryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchDto.getKeyword()));
        }
        if (searchDto.getCatalog3Id() != null) {
            queryBuilder.filter(QueryBuilders.termQuery("catalogId", searchDto.getCatalog3Id()));
        }
        // terms匹配brandId
        if (searchDto.getBrandId() != null && searchDto.getBrandId().size() > 0) {
            queryBuilder.filter(QueryBuilders.termsQuery("brandId", searchDto.getBrandId()));
        }
        // 属性过滤
        if (searchDto.getAttrs() != null && searchDto.getAttrs().size() > 0) {
            for (String attr : searchDto.getAttrs()) {

                String[] s = attr.split("_");
                String attrId = s[0];
                String[] attrValues = s[1].split(":");

                BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();

                nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));
                nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));

                queryBuilder.filter(QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None));

            }
        }
        // 价格区间过滤
//        1、 0_1000 0-100区间
//        2、 1000_ 大于1000
//        3、 _1000 小于1000
        String skuPrices = searchDto.getSkuPrices();
        if (!StringUtils.isEmpty(skuPrices)) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");

            String[] prices = skuPrices.split("_");
            if (prices.length == 2) {
                rangeQueryBuilder.gte(prices[0]).lte(prices[1]);
            } else if (prices.length == 1) {
                if (skuPrices.startsWith("_")) {
                    rangeQueryBuilder.lte(prices[0]);
                } else if (skuPrices.endsWith("_")) {
                    rangeQueryBuilder.gte(prices[0]);
                }
            }

            queryBuilder.filter(rangeQueryBuilder);
        }

        // 是否有库存过滤
        if (searchDto.getHasStock() != null && searchDto.getHasStock() == 1) {
            queryBuilder.filter(QueryBuilders.termQuery("hasStock", true));
        }
        /**
         * 构建：排序、分页、高亮
         */
        // 上小下大
        if (!StringUtils.isEmpty(searchDto.getSort())) {
            String[] sortSplit = searchDto.getSort().split("_");
            // TODO 这里没有做合法校验
            searchSourceBuilder.sort(sortSplit[0], "asc".equalsIgnoreCase(sortSplit[1]) ? SortOrder.ASC : SortOrder.DESC);
        }

        // 分页
        searchSourceBuilder.from((searchDto.getPageNum() - 1) * EsConstant.PRODUCT_PAGE_SIZE);
        searchSourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);

        // 高亮
        if (!StringUtils.isEmpty(searchDto.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        /**
         * 聚合分析
         */
        // 品牌聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(10));
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(10));
        searchSourceBuilder.aggregation(brand_agg);

        // 分类聚合
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(50);
        catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(10));
        searchSourceBuilder.aggregation(catalog_agg);

        // 属性参数聚合
        NestedAggregationBuilder nestedAttrs = AggregationBuilders.nested("attr_agg", "attrs");
        AggregatorFactories.Builder builder = new AggregatorFactories.Builder();
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(10);
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(10));
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(10));
        builder.addAggregator(attr_id_agg);

        nestedAttrs.subAggregations(builder);
        searchSourceBuilder.aggregation(nestedAttrs);
        searchSourceBuilder.query(queryBuilder);
        return new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, searchSourceBuilder);
    }

    /**
     * 解析查询结果
     * @param search
     * @return
     */
    private SearchResultVo resolverResponse(SearchResponse search, SearchDto searchDto) {
        SearchResultVo searchResultVo = new SearchResultVo();
        long totalNumber = search.getHits().getTotalHits().value;
        // 总页数
        searchResultVo.setTotalPages(totalNumber % EsConstant.PRODUCT_PAGE_SIZE == 0 ? totalNumber / EsConstant.PRODUCT_PAGE_SIZE : (totalNumber / EsConstant.PRODUCT_PAGE_SIZE) + 1);
        // 当前页
        searchResultVo.setPageNum(searchDto.getPageNum());
        // 总数
        searchResultVo.setTotal(totalNumber);

        SearchHit[] hits = search.getHits().getHits();
        // SkusElasticModel 添加
        List<SkusElasticModel> skusElasticModels = new ArrayList<>();
        for (SearchHit hit : hits) {
            JSONObject hitJsonObj = JSON.parseObject(hit.getSourceAsString());
            SkusElasticModel skusElasticModel = JSON.toJavaObject(hitJsonObj, SkusElasticModel.class);
            if (!StringUtils.isEmpty(searchDto.getKeyword())) {
                String skuTitle = hit.getHighlightFields().get("skuTitle").getFragments()[0].toString();
                skusElasticModel.setSkuTitle(skusElasticModel.getSkuTitle().replaceAll(searchDto.getKeyword(), skuTitle));
            }
            skusElasticModels.add(skusElasticModel);
        }
        searchResultVo.setSkusElasticModels(skusElasticModels);

        Aggregations aggregations = search.getAggregations();
        // 添加品牌list

        List<SearchResultVo.BrandVo> brandVos = new ArrayList<>();
        ParsedLongTerms brand_agg = aggregations.get("brand_agg");
        List<? extends Terms.Bucket> buckets = brand_agg.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            SearchResultVo.BrandVo brandVo = new SearchResultVo.BrandVo();
            // 设置brandId
            brandVo.setBrandId(bucket.getKeyAsNumber().longValue());

            // 设置品牌图片
            ParsedStringTerms brand_img_agg = bucket.getAggregations().get("brand_img_agg");
            brandVo.setBrandImg(brand_img_agg.getBuckets().get(0).getKeyAsString());

            // 设置品牌名称
            ParsedStringTerms brand_name_agg = bucket.getAggregations().get("brand_name_agg");
            brandVo.setBrandName(brand_name_agg.getBuckets().get(0).getKeyAsString());
            brandVos.add(brandVo);
        }
        searchResultVo.setBrands(brandVos);


        // 添加分类list
        List<SearchResultVo.CatalogVo> catalogVos = new ArrayList<>();
        ParsedLongTerms catalog_agg = aggregations.get("catalog_agg");
        for (Terms.Bucket bucket : catalog_agg.getBuckets()) {
            SearchResultVo.CatalogVo catalogVo = new SearchResultVo.CatalogVo();
            // 设置catalogId
            catalogVo.setCatalogId(bucket.getKeyAsNumber().longValue());

            // 设置分类名
            ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
            catalogVo.setCatalogName(catalog_name_agg.getBuckets().get(0).getKeyAsString());

            catalogVos.add(catalogVo);
        }
        searchResultVo.setCatalogs(catalogVos);

        // 添加属性list
        List<SearchResultVo.AttrVo> attrVos = new ArrayList<>();
        ParsedNested attr_agg = aggregations.get("attr_agg");
        Aggregations attrAgg = attr_agg.getAggregations();

        ParsedLongTerms attr_id_agg = attrAgg.get("attr_id_agg");
        for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
            SearchResultVo.AttrVo attrVo = new SearchResultVo.AttrVo();
            // 设置属性id
            attrVo.setAttrId(bucket.getKeyAsNumber().longValue());
            // 设置属性名
            ParsedStringTerms attr_name_agg = bucket.getAggregations().get("attr_name_agg");
            attrVo.setAttrName(attr_name_agg.getBuckets().get(0).getKeyAsString());
            // 设置属性参数值
            ParsedStringTerms attr_value_agg = bucket.getAggregations().get("attr_value_agg");
            List<String> attrValues = new ArrayList<>();
            for (Terms.Bucket attr_value_aggBucket : attr_value_agg.getBuckets()) {
                attrValues.add(attr_value_aggBucket.getKeyAsString());
            }
            attrVo.setAttrValues(attrValues);
            attrVos.add(attrVo);
        }
        searchResultVo.setAttrs(attrVos);

        List<Integer> thPageArr = new ArrayList<>();
        for (int i = 0; i < searchResultVo.getTotalPages(); i++) {
            thPageArr.add(i + 1);
        }
        searchResultVo.setPageNumArr(thPageArr.toArray(new Integer[thPageArr.size()]));
        return searchResultVo;
    }
}
